`include "PRV564Config.v"
`include "PRV564Define.v"
//////////////////////////////////////////////////////////////////////////////////////////////////
//  Date    : 2021                                                                              //
//  Author  : Jack.Pan                                                                          //
//  Desc    : Page Table Walker For PRV564 processor                                            //
//  Version : 1.0(3rd verision)                                                                      //
//////////////////////////////////////////////////////////////////////////////////////////////////
`include "PRV564Config.v"
`include "PRV564Define.v"

module PTW
#(
    parameter FIB_ID    = 8'h00
)
(
//---------Global Signals-------------
    input wire              PTWi_CLK,
    input wire              PTWi_ARST,
    input wire [1:0]        PTWi_CSR_privlage,  //privlage input
    input wire              PTWi_CSR_sum,
    input wire              PTWi_CSR_mxr,
    input wire [43:0]       PTWi_CSR_satpppn,   //satp's PPN segment
//---------Command & Data signals------------
    input wire [7:0]        PTWi_CMD,           //command 
    input wire              PTWi_V,             //Valid
    input wire [3:0]        PTWi_WalkID,        //Current translation ID
    input wire [26:0]       PTWi_VPN,           //Virtual Address Input
    output wire             PTWo_FULL,          //PTW is full!
//---------PTE and PPN Reply-----------------
    output reg [43:0]       PTWo_PPN,           //Physical Page Number Output(After Translation)
    output reg [7:0]        PTWo_RPL,
    output reg [3:0]        PTWo_WalkID,
    output reg              PTWo_V,
    output reg [9:0]        PTWo_PTE,           //Page Table Entry
    output reg [1:0]        PTWo_PageSize,      //Page size 2:1G, 1:2M, 0:4K
    input wire              PTWi_RDEN,          //read a result
//------------FIB bus interface--------------
    output reg              PTWo_FIB_WREN,      //write to FIB enable
    output reg              PTWo_FIB_REQ,       //request FIB trans
    input wire              PTWi_FIB_ACK,       //request acknowledge
    input wire              PTWi_FIB_FULL,      //FIB FIFO full
    output wire [7:0]       PTWo_FIB_ID,
    output reg  [7:0]       PTWo_FIB_CMD,
    output wire [3:0]       PTWo_FIB_BURST,
    output wire [3:0]       PTWo_FIB_SIZE,
    output reg [`XLEN-1:0]  PTWo_FIB_ADDR,      
    output reg [`XLEN-1:0]  PTWo_FIB_DATA,
    input wire [7:0]        PTWi_FIB_ID,
    input wire [7:0]        PTWi_FIB_RPL,
    input wire              PTWi_FIB_V,
    input wire [`XLEN-1:0]  PTWi_FIB_DATA
);

    localparam stb 	    = 4'h0;	                //等待状态，当需要进行页面检查时候跳转
    localparam s2_1	    = 4'h1;	                //访问页表
    localparam s2_2	    = 4'h2;
    localparam s4  	    = 4'h3;	                //页面有效性检查，如果是指针则继续进行页表访问
    localparam s5  	    = 4'h4;	                //页面权限检查，如果权限不符合则直接引起页面异常
    localparam pte_upd0 = 4'h5;	                //页表更新，如果A不为1，则将A置1
    localparam pte_upd1 = 4'h6;
    localparam ready	= 4'h7;		            //转换完成
    localparam page_fault= 4'h8;
//---------------输入的转换命令首先被寄存---------------------
    reg [7:0]        CMD;                   //command 
    reg              V;                     //Valid
    reg [3:0]        WalkID;                //Current translation ID
    reg [26:0]       VPN;                   //Virtual Address Input
    reg [1:0]        privlage;              //privlage input
    reg              sum;
    reg              mxr;
    reg [43:0]       satpppn;               //satp's VPN segment
//-----------------转换用的临时寄存器------------------------
    reg [3:0]       state;                  //state Machine of PTW
    reg [1:0]       i;
    reg [`XLEN-1:0] PTE_temp;               //临时保存的PTE
    reg [43:0]      a;                      //A寄存器
    wire            page_checkOK;
//------------------输入命令首先寄存一排-------------------------------
always@(posedge PTWi_CLK or posedge PTWi_ARST)begin
    if(PTWi_ARST)begin
        V <= 1'b0;
    end
    else if(V)begin                         //当前有一个正在转换的表项,当转换完成后自动清零
        V <= ((state==page_fault) | (state==ready)) ? 1'b0 : 1'b1;
    end
    else begin
        V           <= PTWi_V;
        CMD         <= PTWi_CMD;               //command 
        WalkID      <= PTWi_WalkID;            //Current translation ID
        VPN         <= PTWi_VPN;               //Virtual Address Input
        privlage    <= PTWi_CSR_privlage;      //privlage input
        sum         <= PTWi_CSR_sum;
        mxr         <= PTWi_CSR_mxr;
        satpppn     <= PTWi_CSR_satpppn;       //satp's VPN segment
    end
end
assign PTWo_FULL = V;                           //当前有一个正在等待转换的页表，满
//------------------Page Table walk 状态机----------------------------
always@(posedge PTWi_CLK or posedge PTWi_ARST)begin
    if(PTWi_ARST)begin
        state <= stb;
    end
    else begin
        case(state)
            stb     :   if(V & !PTWo_V)begin        //如果输出保持的值已被取走，则继续转换
                            state <= s2_1;
                            i     <= 2'h2;          //reset i
                            a     <= satpppn;       //A = satp ppn
                        end
            s2_1    :   if(PTWi_FIB_ACK & !PTWi_FIB_FULL)begin          //如果成功写入FIB桥，则转到下一状态，等待回复数据包
                            state <= s2_2;
                        end
            s2_2    :   if(((PTWi_FIB_ID == FIB_ID)|(PTWi_FIB_ID==8'h00)) & PTWi_FIB_V)begin   //
                            case(PTWi_FIB_RPL)
                                `FIB_RPL_IDLE : state <= s2_1;          //如果收到IDLE包，表明数据包丢失，请求重传
                                `FIB_RPL_TERR : state <= page_fault;    //页面错误
                                `FIB_RPL_TRDY : state <= s4;
                                default       : state <= state;
                            endcase   
                        end
            s4      :   if(!PTE_temp[`Sv39_V] | !PTE_temp[`Sv39_R] & PTE_temp[`Sv39_W])begin
                            state <= page_fault;                    //如果V==0或者R、W==0、0、1，造成错误页面
                        end
                        else if(PTE_temp[`Sv39_R] | PTE_temp[`Sv39_X])begin
                            state <= s5;                            //如果R==1或x==1，则是一个末端页面，转到s5
                        end
                        else if(i==2'h0)begin
                            state <= page_fault;                    //如果i=0，已经无法继续转换，造成错误页面
                        end
                        else begin: NextPTE                         //如果这是一个末端页面，则继续进行
                            state <= s2_1;
                            i     <= i - 2'h1;
                            a     <= PTE_temp[53:10];
                        end
            s5      :   if(!page_checkOK)begin
                            state <= page_fault;                    //如果页面检查失败，则直接造成页面错误
                        end
                        else if(((i==2'h1)&PTE_temp[18:10]!=9'b0)|((i==2'h2)&PTE_temp[27:10]!=18'b0))begin  //若页面是一个对齐的超页面，则引起页面错误
                            state <= page_fault;
                        end
                        else if(!PTE_temp[`Sv39_A] | (CMD==`TLB_CMD_wLUT)&!PTE_temp[`Sv39_D])begin               //若页面的对应位没有置位，则置位
                            state <= pte_upd0;
                        end
                        else begin
                            state <= ready;
                        end
            pte_upd0:   state <= pte_upd1;
            pte_upd1:   if(((PTWi_FIB_ID == FIB_ID)|(PTWi_FIB_ID==8'h00)) & PTWi_FIB_V)begin   //
                            case(PTWi_FIB_RPL)
                                `FIB_RPL_IDLE : state <= pte_upd0;          //如果收到IDLE包，表明数据包丢失，请求重传
                                `FIB_RPL_TERR : state <= page_fault;        //页面错误
                                `FIB_RPL_TRDY : state <= ready;
                                default       : state <= state;
                            endcase   
                        end
		    ready:	state <= stb;
            
		    page_fault:	state <= stb;

            default:state <= stb;
        endcase
    end
end
//----页面检查------
PageCheck           PTW_PageCheck(
//csr
    .CSR_priv               (privlage),           //CPU privlage input
    .CSR_mxr                (mxr),            //CSR MXR bit input
    .CSR_sum                (sum),            //CSR SUM bit input
//read or write
    .OP_read                (CMD==`TLB_CMD_rLUT),            //operations
    .OP_write               (CMD==`TLB_CMD_wLUT),
    .OP_execute             (CMD==`TLB_CMD_xLUT),
    .PTE_U                  (PTE_temp[`Sv39_U]),
    .PTE_W                  (PTE_temp[`Sv39_W]),
    .PTE_R                  (PTE_temp[`Sv39_R]),
    .PTE_X                  (PTE_temp[`Sv39_X]),
    .PTE_D                  (PTE_temp[`Sv39_D]),
    .check_ok               (page_checkOK)
);
//PTE_temp寄存器更新
always@(posedge PTWi_CLK or posedge PTWi_ARST)begin
    if(PTWi_ARST)begin
        PTE_temp <= 64'h0;
    end
    	//页面更新时候 pte temp被更新为TLB送出的页表；或者是自身页表置A
	else if(state==s5)begin
		if(!PTE_temp[`Sv39_A])begin
            PTE_temp[`Sv39_A] <= 1'b1;
        end
        if(!PTE_temp[`Sv39_D] & (CMD==`TLB_CMD_wLUT))begin
            PTE_temp[`Sv39_D] <= 1'b1;
        end
	end
	else if(state==s2_2)begin
		PTE_temp <= PTWi_FIB_DATA;
	end
end
//----------------------------输出寄存器-----------------------------
always@(posedge PTWi_CLK or posedge PTWi_ARST)begin
    if(PTWi_ARST)begin
        PTWo_V <= 1'b0;
    end
    else if(PTWo_V)begin
        PTWo_V <= PTWi_RDEN ? 1'b0 : PTWo_V;
    end
    else begin
        if(state==ready)begin
            PTWo_V          <= 1'b1;
            PTWo_WalkID     <= WalkID;
            PTWo_RPL        <= `TLB_PRL_RDY;
            PTWo_PPN        <= PTE_temp[53:10];
            PTWo_PTE        <= PTE_temp[9:0];
            PTWo_PageSize   <= i;
        end
        else if(state==page_fault)begin
            case(CMD)
                `TLB_CMD_rLUT   :
                begin
                    PTWo_V          <= 1'b1;
                    PTWo_WalkID     <= WalkID;
                    PTWo_RPL        <= `TLB_RPL_rPERR;
                    PTWo_PPN        <= PTE_temp[53:10];
                    PTWo_PTE        <= PTE_temp[9:0];
                    PTWo_PageSize   <= i;
                end
                `TLB_CMD_wLUT   :   
                begin 
                    PTWo_V          <= 1'b1;
                    PTWo_WalkID     <= WalkID;
                    PTWo_RPL        <= `TLB_RPL_wPERR;
                    PTWo_PPN        <= PTE_temp[53:10];
                    PTWo_PTE        <= PTE_temp[9:0];
                    PTWo_PageSize   <= i;
                end 
                `TLB_CMD_xLUT   :
                begin
                    PTWo_V          <= 1'b1;
                    PTWo_WalkID     <= WalkID;
                    PTWo_RPL        <= `TLB_RPL_xPERR;
                    PTWo_PPN        <= PTE_temp[53:10];
                    PTWo_PTE        <= PTE_temp[9:0];
                    PTWo_PageSize   <= i;
                end
                default         :
                begin
                    PTWo_V          <= 1'b0;
                    PTWo_WalkID     <= 4'hx;
                    PTWo_RPL        <= 8'hx;
                    PTWo_PPN        <= 44'hx;
                    PTWo_PTE        <= 10'hx;
                    PTWo_PageSize   <= 2'hx;
                end
            endcase
        end
    end
end
//---------------------------------------FIB总线接口-------------------------------------------
assign PTWo_FIB_ID      = FIB_ID;
assign PTWo_FIB_BURST   = 4'h0;         //No Burst lengeh is need
assign PTWo_FIB_SIZE    = 4'h3;         //8 Byte is use
always@(*)begin
    case(state)
        s2_1    :   
        begin
            PTWo_FIB_CMD = `FIB_CMD_SIGR;
            PTWo_FIB_REQ = 1'b1;
            PTWo_FIB_WREN = PTWi_FIB_FULL ? 1'b0 : 1'b1;
            PTWo_FIB_DATA = PTE_temp;
        end
        pte_upd0:   
        begin
            PTWo_FIB_CMD = `FIB_CMD_SIGW;
            PTWo_FIB_REQ = 1'b1;
            PTWo_FIB_WREN = PTWi_FIB_FULL ? 1'b0 : 1'b1;
            PTWo_FIB_DATA = PTE_temp;
        end
    default     :   
        begin
            PTWo_FIB_CMD = `FIB_CMD_NOOP;
            PTWo_FIB_REQ = 1'b0;
            PTWo_FIB_WREN = 1'b0;
            PTWo_FIB_DATA = 64'h0;
        end
    endcase
end
always@(*)begin
    case(i)
        2'h0    :   PTWo_FIB_ADDR = {8'b0,a,VPN[8:0],3'b0};
        2'h1    :   PTWo_FIB_ADDR = {8'b0,a,VPN[17:9],3'b0};
        2'h2    :   PTWo_FIB_ADDR = {8'b0,a,VPN[26:18],3'b0};
    default     :   PTWo_FIB_ADDR = 64'h0;
    endcase
end

endmodule
